import json
import re

import logging
from django import http
from django.contrib.auth import login, authenticate, logout
from django.core.paginator import EmptyPage
from django.core.paginator import Paginator

from django.shortcuts import render, redirect
from carts.utils import merge_cart_cookie_to_redis
from goods.models import SKU
from orders.models import OrderInfo
from . import constants
# Create your views here.
from django.urls import reverse
from django.views import View
from pymysql import DatabaseError
from .utils import generate_verify_email_url, check_verify_email_token
from meiduo_mall.utils.response_code import RETCODE
from meiduo_mall.utils.views import LoginRequiredJSONMixin
from users.models import User, Address
from django_redis import get_redis_connection
from celery_tasks.emails.tasks import send_verify_email
from django.contrib.auth.mixins import  LoginRequiredMixin
logger = logging.getLogger('django')
class RegisterView(View):
    def get(self,request):


        return render(request,'register.html')

    def post(self,request):
        username = request.POST.get('username')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        mobile = request.POST.get('mobile')
        allow = request.POST.get('allow')
        sms_code_client =request.POST.get('sms_code')

# 判断参数是否齐全
        if not all([username,password,password2,mobile,allow]):
            return http.HttpResponseForbidden('缺少必传参数')
 # 判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入5-20字的用户名')
# 判断密码是否是8-20个数字
        if not re.match(r'^[a-zA-Z0-9_-]{8,20}$',password):
            return http.HttpResponseForbidden('请输入8-20字的密码')
# 判断两次密码是否一致
        if password2 != password:
            return http.HttpResponseForbidden('两次密码不一致')
    # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('请输入正确的电话号码')
    #判断验证码是否正确
    # 1从redis中取出数据
        redis_coon = get_redis_connection('verify_code')
        sms_code_server = redis_coon.get('sms_%s'% mobile)
        if sms_code_server is None:
            return render(request,'register.html',{'sms_code_errmsg':'无效的短信验证码'})
        if sms_code_client != sms_code_server.decode():
            return render(request,'register.html',{'sms_code_errmsg':'验证码错误'})
    # 判断是否勾选用户协议
        if allow != 'on':
            return http.HttpResponseForbidden('请勾选用户协议')

        # try:
        #     User.objects.create_user(username=username,password=password,mobile=mobile)
        # except DatabaseError:
        #     return
        try:
            user=User.objects.create_user(username=username, password=password,mobile=mobile)
        except DatabaseError:
            return render(request, 'register.html', {'register_errmsg': '注册失败'})

        login(request,user)
        # 登录状态保持

        # 相应注册结果
        return redirect(reverse('contents:index'))

class UsernameView(View):
    # 判断用户名是否重复注册
    def get(self,request,username):
        # 参数包括请求对象request和请求内容username

        count = User.objects.filter(username=username).count()
        # 返回json函数
        return http.JsonResponse({'cont':RETCODE.OK,'errmsg':'ok','count':count})
class MobileView(View):
    # 判断手机号是否重复注册
    def get(self,request,mobile):
        count = User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'ok','count':count})

class LoginView(View):
    # 提供登陆界面
    def get(self,request):
        return render(request,'login.html')
    # 实现登录功能
    def post(self,request):
        # 接受参数
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')
        # 校验参数
        # 校验参数是否完整
        if not all([username,password]):
            return http.HttpResponseForbidden('缺少必传参数')
        # 判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9]{5,20}$',username):
            return http.HttpResponseForbidden('请输入正确的用户名')
         # 判断密码是否是8-20个数字
        if not re.match(r'^[a-zA-Z0-9]{8,20}$',password):
            return http.HttpResponseForbidden('请输入正确的密码')
        # 认证登录用户
        user = authenticate(username=username,password=password)
        if user is None:
            return render(request,'login.html',{'account_errmsg':'用户名或密码错误'})
        # 实现状态保持
        login(request,user)
        # 设置状态保持的周期
        if remembered != 'on':
            # 没有记住用户则推出就过期
            request.session.set_expiry(0)
        else:
            request.session.set_expiry(None)
        # 注册时用户名写入到cookie，有效期15天\
        next = request.GET.get('next')
        if next:
            response = redirect(next)
        else:
            response = redirect(reverse('contents:index'))
        response.set_cookie('username',user.username,max_age=60*60*24*15)
        #合并购物车
        response = merge_cart_cookie_to_redis(request=request, user=user, response=response)
        # 响应注册结果
        return response

# class LogoutView(View):
# #     退出登录
#     def get(self,request):
#         # 清理session
#         logout(request)
#         # 退出登录，重定向到登录页
#         response = redirect(reverse('contents:index'))
#         # 退出登录时清除cookie中的username
#         response.delete_cookie('username')
#         return response
class LogoutView(View):
    """退出登录"""

    def get(self, request):
        """实现退出登录逻辑"""
        # 清理session
        logout(request)
        # 退出登录，重定向到登录页
        response = redirect(reverse('contents:index'))
        # 退出登录时清除cookie中的username
        response.delete_cookie('username')
        return response


class UserInfoView(LoginRequiredMixin,View):
    def get(self,request):
        """提供个人信息界面"""
        '''如果判断为已经登录，那么request.user就是登录用户对象'''
        context = {
            'username':request.user.username,
            'mobile':request.user.mobile,
            'email_active':request.user.email_active,
            'email': request.user.email,
        }
        '''响应登录结果'''
        return render(request,'user_center_info.html',context)

class EmailView(LoginRequiredJSONMixin,View):
    '''添加邮箱'''
    def put(self,request):
        """实现添加邮箱逻辑"""

        '''1.接收参数'''
        json_dict = json.loads(request.body.decode())
        email = json_dict.get('email')

        '''2.校验参数'''
        if not email:
            return http.HttpResponseForbidden('缺少email参数')
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.HttpResponseForbidden('输入的email参数有误')
        '''3.保存参数，添加邮箱字段'''
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code' : RETCODE.DBERR,'errmsg':'添加邮箱失败'})
        '''异步发送邮箱的验证邮件'''
        verify_url = generate_verify_email_url(request.user)
        send_verify_email.delay(email, verify_url)
        '''4.响应结果'''
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'添加邮箱成功'})

class VerifyEmailView(View):
    '''验证邮箱'''
    def get(self,request):
        '''接受参数'''
        token = request.GET.get('token')
        '''校验参数：判断token是否为空和过期，提取user'''
        if not token :
            return http.HttpResponseBadRequest('缺少token')
        '''从token中获取用户信息：通过user_id确定user'''
        user = check_verify_email_token(token)
        if not user:
            return http.HttpResponseForbidden('无效的token')
        '''将用户的email_active设置为true'''
        try:
            user.email_active = True
            user.save()
        except:
            return http.HttpResponseServerError('激活失败')
        '''响应结果,返回到个人信息页面'''
        return redirect(reverse('users:info'))

'''设置收获地址，只有登录过之后才能设置，所以要通过mixin父类来判断是否已经登录'''
class AddressView(LoginRequiredMixin,View):
    '''设置收获地址页面'''
    def get(self,request):
        # 获取用户地址列表
        #获取当前登陆用户对象
        login_user = request.user#request.user就是当前登陆用户
        addresses = Address.objects.filter(user = login_user,is_deleted = False)
        #将模型列表转化为字典列表因为vue.js和Jsonresponse不认识模型列表，jinja2或者django可以用
        address_list = []
        for address in addresses:
            address_dict={
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }

            address_list.append(address_dict)
        # 设置上下文

        context = {
            'default_address_id': login_user.default_address_id,
            'addresses': address_list,
        }

        return render(request,'user_center_site.html',context)
    #登陆之后才能新增地址
class AddressCreateView(LoginRequiredJSONMixin,View):
    '''新增收货地址'''
    def post(self,request):
        '''实现新增地址逻辑'''
        # 判断是否超过地址上限：最多20个
        count = request.user.addresses.count()
        if count >= constants.USER_ADDRESS_COUNTS_LIMIT:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '超过地址数量上限'})
        #     # 接收参数
        # 接收参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 保存地址信息
        try:
            address = Address.objects.create(
                user=request.user,
                title = receiver,
                receiver = receiver,
                province_id = province_id,
                city_id = city_id,
                district_id = district_id,
                place = place,
                mobile = mobile,
                tel = tel,
                email = email,)
            #没有默认地址则设置为默认地址
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'新增地址失败'})
        # 新增地址成功，将新增的地址响应给前端实现局部刷新
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        # 返回结果
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'新增地址成功','address':address_dict})

class UpdateDestroyAddressView(LoginRequiredJSONMixin, View):
    """修改和删除地址"""
    def put(self,request,address_id):
        '''接受参数'''
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')
        '''校验参数'''
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

            '''判断地址是否存在，并更新信息'''
        try:
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'更新地址失败'})
            # '''将地址返回前段渲染'''
        address = Address.objects.get(id = address_id)
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'ok','address':address_dict})

    def delete(self,request,address_id):
        try:
            address = Address.objects.get(id = address_id)
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'删除地址失败'})
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'删除地址成功'})

class DefaultAddressView(LoginRequiredJSONMixin, View):
    def put(self,request,address_id):
        try:
    # 接收参数,查询地址
            address = Address.objects.get(id = address_id)
    # 校验参数，设置位默认地址
            request.user.default_address =address
            request.user.save()
    #响应结果
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'设置地址失败'})
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'设置默认地址成功'})

class UpdateTitleAddressView(LoginRequiredJSONMixin,View):
    def put(self,request,address_id):

    # 接收参数：地址标题
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')

    ## 查询地址
        try:
            address = Address.objects.get(id = address_id)
        #设置新的地址标题
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'更新地址标题失败'})
    #响应结果
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'更新标题成功'})

class ChangePasswordView(LoginRequiredJSONMixin,View):
    def get(self,request):
        return redirect(reverse('user_center_pass.html'))

    def post(self,request):
        '''接受参数'''
        old_password = request.POST.get('old_password')
        new_password = request.POST.get('new_password')
        new_password2 = request.POST.get('new_password2')

        '''校验参数'''
        if not all([old_password,new_password,new_password2]):
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'缺少必要参数'})
        try:
            request.user.check_password(old_password)
        except Exception as e:
            logger.error(e)
            return render(request,'user_center_pass.html',{'origin_pwd_errmsg':'原始密码错误'})

        if not re.match(r'^[a-zA-Z0-9]{8,20}$',new_password):
            return http.HttpResponseForbidden('请输入正确的密码')

        if new_password2 != new_password:
            return http.HttpResponseForbidden('2次输入密码不一致')

        '''修改密码'''
        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'修改密码失败'})

        '''清楚状态保持信息'''
        logout(request)
        '''重定向到登陆界面'''
        response = render(request,'users:login')
        response.delete_cookie('username')
        return response

class UserBrowseHistory(LoginRequiredJSONMixin, View):
    """用户浏览记录"""

    def post(self, request):
        """保存用户浏览记录"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        # 校验参数
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku不存在')
        # 保存用户浏览数据
        redis_conn = get_redis_connection('history')
        pl = redis_conn.pipeline()#pl是管道，防止有操作失败导致后续操作无法执行
        user_id = request.user.id
        # 先去重
        pl.lrem('history_%s' % user_id, 0, sku_id)
        # 再存储
        pl.lpush('history_%s' % user_id, sku_id)
        # 最后截取
        pl.ltrim('history_%s' % user_id, 0, 4)
        # 执行管道
        pl.execute()

        # 响应结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})

    def get(self, request):
        """获取用户浏览记录"""
        user = request.user
        # 获取Redis存储的sku_id列表信息
        redis_conn = get_redis_connection('history')
        sku_ids = redis_conn.lrange('history_%s' % user.id, 0, -1)

        # 根据sku_ids列表数据，查询出商品sku信息
        skus = []
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
            skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'skus': skus})


class UserOrderInfoView(LoginRequiredMixin, View):
    """我的订单"""

    def get(self, request, page_num):
        """提供我的订单页面"""
        user = request.user
        # 查询订单
        orders = user.orderinfo_set.all().order_by("-create_time")
        # 遍历所有订单
        for order in orders:
            # 绑定订单状态
            order.status_name = OrderInfo.ORDER_STATUS_CHOICES[order.status-1][1]
            # 绑定支付方式
            order.pay_method_name = OrderInfo.PAY_METHOD_CHOICES[order.pay_method-1][1]
            order.sku_list = []
            # 查询订单商品
            order_goods = order.skus.all()
            # 遍历订单商品
            for order_good in order_goods:
                sku = order_good.sku
                sku.count = order_good.count
                sku.amount = sku.price * sku.count
                order.sku_list.append(sku)

        # 分页
        page_num = int(page_num)
        try:
            paginator = Paginator(orders, constants.ORDERS_LIST_LIMIT)
            page_orders = paginator.page(page_num)
            total_page = paginator.num_pages
        except EmptyPage:
            return http.HttpResponseNotFound('订单不存在')

        context = {
            "page_orders": page_orders,
            'total_page': total_page,
            'page_num': page_num,
        }
        return render(request, "user_center_order.html", context)